<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AI Adoption Rolling Avg — Pyodide</title>
    <style>
      :root { color-scheme: light; }
      html, body { height: 100%; }
      body { margin: 0; background: #fff; color: #111; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Inter, "Helvetica Neue", Arial; }
      .wrap { max-width: 980px; margin: 0 auto; padding: 24px; }
      header { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:16px; }
      h1 { font-size: clamp(18px, 2.4vw, 24px); margin: 0; font-weight: 650; letter-spacing: .2px; }
      .controls { display:flex; gap: 8px; align-items:center; }
      button, a.btn { appearance: none; border: 1px solid #ddd; background: #fff; color: #111; padding: 8px 12px; border-radius: 8px; font-weight: 600; cursor: pointer; text-decoration: none; }
      button:disabled, .btn.disabled { color: #9aa0a6; border-color: #eee; background:#fafafa; cursor: default; pointer-events: none; }
      .grid { display: grid; grid-template-columns: 1fr; gap: 16px; }
      @media (min-width: 900px){ .grid { grid-template-columns: 340px 1fr; } }
      .card { border: 1px solid #e5e7eb; border-radius: 10px; padding: 14px; background: #fff; }
      pre.status { margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; line-height: 1.45; color: #333; white-space: pre-wrap; }
      .imgwrap { display:grid; place-items:center; min-height: 460px; background: #fff; border: 1px dashed #e5e7eb; border-radius: 10px; }
      img { max-width: 100%; height: auto; display: block; }
      footer { margin-top: 12px; font-size: 12px; color:#555; }
    </style>
  </head>
  <body>
    <div class="wrap">
      <header>
        <h1>AI adoption — 6‑survey rolling average</h1>
        <div class="controls">
          <button id="run" disabled>Run</button>
          <a id="dl_png" class="btn disabled" href="#">Download PNG</a>
          <a id="dl_svg" class="btn disabled" href="#">Download SVG</a>
        </div>
      </header>

      <div class="grid">
        <div class="card">
          <pre id="status" class="status"></pre>
        </div>
        <div class="imgwrap card">
          <img id="plot" alt="Chart will render here" />
        </div>
      </div>

      <footer>Source: "Employment Size Class (Sep 2025)" — fetched via CORS from static.simonwillison.net</footer>
    </div>

    <script src="https://cdn.jsdelivr.net/pyodide/v0.26.2/full/pyodide.js"></script>
    <script>
      const runBtn = document.getElementById('run');
      const statusEl = document.getElementById('status');
      const pngA = document.getElementById('dl_png');
      const svgA = document.getElementById('dl_svg');

      function log(x){ statusEl.textContent += x + "\n"; }
      function enableDownloadLinks(){ pngA.classList.remove('disabled'); svgA.classList.remove('disabled'); }

      const PY = `\
import pandas as pd
import numpy as np
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt
from matplotlib.ticker import PercentFormatter
from js import document, encodeURIComponent
import io, base64

URL = 'https://static.simonwillison.net/static/cors-allow/2025/Employment-Size-Class-Sep-2025.xlsx'

# Fetch workbook bytes via pyfetch (binary-safe), then read both sheets
from pyodide.http import pyfetch
resp_fetch = await pyfetch(URL)
wb_bytes = await resp_fetch.bytes()
xf = pd.ExcelFile(io.BytesIO(wb_bytes), engine='openpyxl')
resp = xf.parse('Response Estimates')
dates = xf.parse('Collection and Reference Dates')

# Filter to current question + yes
is_current = resp['Question'].astype(str).str.strip().str.startswith('In the last two weeks')
ai_yes = resp[is_current & resp['Answer'].astype(str).str.strip().str.lower().eq('yes')].copy()

code_to_bucket = {
  'A':'1-4','B':'5-9','C':'10-19','D':'20-49','E':'50-99','F':'100-249','G':'250 or more employees'
}
ai_yes['Bucket'] = ai_yes['Empsize'].map(code_to_bucket)

period_cols = [c for c in ai_yes.columns if str(c).isdigit() and len(str(c))==6]
long = ai_yes.melt(id_vars=['Bucket'], value_vars=period_cols, var_name='Smpdt', value_name='value')

dates['Smpdt'] = dates['Smpdt'].astype(str)
long['Smpdt'] = long['Smpdt'].astype(str)
merged = long.merge(dates[['Smpdt','Ref End']], on='Smpdt', how='left')
merged['date'] = pd.to_datetime(merged['Ref End'], errors='coerce')

merged['value'] = pd.to_numeric(long['value'].astype(str).str.replace('%','',regex=False).str.strip(), errors='coerce')

order = ['250 or more employees','100-249','50-99','20-49','10-19','5-9','1-4']
wide = merged.pivot_table(index='date', columns='Bucket', values='value', aggfunc='mean').sort_index()
wide = wide[[c for c in order if c in wide.columns]]
rolled = wide.rolling(window=6, min_periods=6).mean()

start, end = pd.Timestamp('2023-11-01'), pd.Timestamp('2025-08-31')
rolled_win = rolled.loc[(rolled.index >= start) & (rolled.index <= end)]

fig, ax = plt.subplots(figsize=(12, 6))
for col in order:
    if col in rolled_win.columns:
        ax.plot(rolled_win.index, rolled_win[col], label=col, linewidth=2)

ax.set_title('AI adoption (last two weeks) — 6‑survey rolling average', pad=12)
ax.yaxis.set_major_formatter(PercentFormatter(100))
ax.set_ylabel('%')
ax.set_xlabel('')
ax.grid(True, alpha=0.25, linestyle='--')
ax.legend(title=None, loc='upper left', ncol=2, frameon=False)
fig.tight_layout()

# PNG
buf_png = io.BytesIO()
fig.savefig(buf_png, format='png', dpi=200, bbox_inches='tight')
buf_png.seek(0)
img_b64 = base64.b64encode(buf_png.getvalue()).decode('ascii')
document.getElementById('plot').src = 'data:image/png;base64,' + img_b64

# SVG
svg_buf = io.StringIO()
fig.savefig(svg_buf, format='svg', bbox_inches='tight')
svg_text = svg_buf.getvalue()

# Expose download links
document.getElementById('dl_png').href = 'data:image/png;base64,' + img_b64
document.getElementById('dl_png').download = 'ai_adoption_rolling6_by_firm_size.png'
document.getElementById('dl_svg').href = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg_text)
document.getElementById('dl_svg').download = 'ai_adoption_rolling6_by_firm_size.svg'
`;

      let pyodide = null;

      async function boot(){
        log('Loading Python…');
        pyodide = await loadPyodide({ indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.26.2/full/' });
        log('Fetching packages: numpy, pandas, matplotlib…');
        await pyodide.loadPackage(['numpy','pandas','matplotlib','micropip']);
        log('Installing openpyxl via micropip…');
        await pyodide.runPythonAsync(`import micropip
await micropip.install("openpyxl")`);
        log('Ready.');
        runBtn.disabled = false;
        await run(); // auto-run once
      }

      async function run(){
        if (!pyodide) return;
        pngA.classList.add('disabled'); svgA.classList.add('disabled');
        try {
          log('Running…');
          await pyodide.runPythonAsync(PY);
          log('Done.');
          enableDownloadLinks();
        } catch (e){
          console.error(e); log('Error: ' + e);
        }
      }

      runBtn.addEventListener('click', run);
      boot();
    </script>
  </body>
</html>
